#include <stdlib.h>
#include <stdio.h>
#include <semaphore.h>

#include "../header/bib_Log.h"
#include "../header/bib_Abrir_Configuracion.h"
#include "../header/bib_Socket.h"
#include "../header/bib_Comunicacion.h"
#include "../header/bib_Cache.h"

//Variables Globales
char nivelLog;
int flagSalir=1;		//0: Salir; 1:Continuar
char usarConsola='F';	//Por defecto, habilitado y completo
char usarCache='S';		//Por defecto, habilitado
int grupoEspejo=100;	//Por defecto

//Cache
struct stCACHE memCache[TAM_CACHE];
sem_t accesoCache;

//Estructura de planificador
struct EST_CHS CHSPlanif;
char esperaCHS=0;
sem_t accesoCHS;

//Identificadores para las solicitudes
uint32_t proximoIdPlanif = SOLICITUD_MIN;
sem_t accesoIdPlanif;

//Lista de planificadores
struct stPLANIFICADOR *lsPlanif = NULL;
int cantListaPlanif=0;
sem_t accesoListaPlanif;

//Lista de estructura de File System
struct stFILE_SYSTEM *lsFS=NULL;
sem_t accesoListaFS;

//Log
sem_t accesoLog;

//Select
fd_set todos;
int maxSock=0;

//Prototipos
void actualizarMax(int *max, int nuevoSock);
int inicializarSemaforos();
void esperarHilos();

int main(void){
	struct CONFIG_EST configInicial;
	int socketEntrada=0;
	int nuevaConexion;

	//Select
	fd_set preparados;
	struct timeval tiempoSelect;
	int i;

	//Inicializar semáforos
	if (inicializarSemaforos()){
		imprimirLogSinSem(ERROR, "No se pudo inicializar los semaforos.","");
		return FIN_ERROR;
	}

	//Inicializar CHS
	CHSPlanif.cabezal=0;
	CHSPlanif.cilindro=0;
	CHSPlanif.sector=0;

	//Cargar configuración
	if (abrirConfiguracion(&configInicial)==FIN_ERROR){
		//Error
		return FIN_ERROR;
	}

	//Inicializar cache
	inicializarCache();

	//Iniciar socket
	if (abrirSocketServidor(&socketEntrada,configInicial.puertoEntrada)==FIN_ERROR){
		//Error
		return FIN_ERROR;
	}

	//Inicializar Select
	actualizarMax(&maxSock, socketEntrada);
	FD_ZERO(&todos);
	FD_SET(socketEntrada,&todos);
	tiempoSelect.tv_usec=0;

	imprimirLog(DEBUG, "Esperando conexiones..", "(Select)");

	while(flagSalir){
		//Reinicializar tiempo
		tiempoSelect.tv_sec=TIEMPO_SELECT;

		//Recibir conexión
		preparados=todos;
		select(maxSock, &preparados, NULL, NULL, &tiempoSelect);

		//Determinar lo sockets preparados
		for(i=1;i<maxSock;i++){

			if (FD_ISSET(i,&preparados)){
				//Socket preparado
				if (i==socketEntrada){
					//Nueva Conexión
					if (atenderNuevaConexion(socketEntrada,&nuevaConexion)==FIN_OK){
						//Si no ocurrio error, preparar para Select
						FD_SET(nuevaConexion,&todos);
						actualizarMax(&maxSock, nuevaConexion);
					}

				}else{

					//Nuevo mensaje
					if (atenderNuevoMensaje(i)){
						//Si se desconecto, quitar de la lista de conectados y actualizar maximo
						quitarDeSelect(i);
					}
				}
			}
		}
	}

	imprimirLog(DEBUG, "Se solicito finalizar el proceso.", "(Flag)");

	//Esperar que los hilos
	esperarHilos();

	return FIN_OK;
}

//Actualiza valor max de Select
void actualizarMax(int *max, int nuevoSock){
	if (nuevoSock>((*max)-1))
		*max=nuevoSock+1;
}

//Inicializa todos los semáforos
int inicializarSemaforos(){
	if (sem_init(&accesoListaPlanif, 0, 1))
		return FIN_ERROR;
	if (sem_init(&accesoCHS, 0, 1))
		return FIN_ERROR;
	if (sem_init(&accesoLog, 0, 1))
		return FIN_ERROR;
	if (sem_init(&accesoIdPlanif, 0, 1))
		return FIN_ERROR;
	if (sem_init(&accesoListaFS, 0, 1))
		return FIN_ERROR;
	if (sem_init(&accesoCache, 0, 1))
		return FIN_ERROR;

	return FIN_OK;
}

/*////Solo para testeo
void mostrarListasPlanif(){
	//Recorrer la lista (Deberia obtener el semaforo de acceso a la lista en esta funcion)
	struct stPLANIFICADOR *planif;
	struct stSOLICITUD *solic;
	int cont;

	//Por cada planificador
	planif=lsPlanif;
	while (planif != NULL){
		printf("Planificador: %u\n",planif->idPlanificador);

		sem_wait(&planif->accesoPlanif);

		//Por cada solicitud pendiente
		cont=0;
		solic=planif->solicitudPend;
		while (solic != NULL){
			cont++;
			solic=solic->sgte;
		}
		printf("***Solicitudes pendientes: %d\n",cont);

		//Por cada solicitud pendiente para espejar
		cont=0;
		solic=planif->solicitudPendEspejar;
		while (solic != NULL){
			cont++;
			solic=solic->sgte;
		}
		printf("***Solicitudes pendientes para espejar: %d\n",cont);

		//Por cada solicitud enviada
		cont=0;
		solic=planif->solicitudEnv;
		while (solic != NULL){
			cont++;
			solic=solic->sgte;
		}
		printf("***Solicitudes enviadas: %d\n",cont);

		sem_post(&planif->accesoPlanif);

		planif=planif->sgte;
	}
}*/

//Espera a que todos los hilos finalicen antes de terminar el programa
void esperarHilos(){
	int cantPlanif;

	cantPlanif=obtenerCantidadPlanif();

	if (cantPlanif>0){
		sleep(TIEMPO_SLEEP);
	}
}
